<?php

declare(strict_types=1);

namespace App\Utils;

use Hyperf\Coroutine\Coroutine;

/**
 * Author:Panda
 * Email:joeyoung0314@qq.com
 * Class LogUtil
 * @package App\Utils
 *
 * ========================================================================
 * +    根据协程
 * +
 * +
 * +
 * ========================================================================
 */
class LogUtil
{
    /**==============日志系统==============**/
    private static int $size = 2;// (单位M) 避免单个日志文件太大 请设置单个文件最大存储

    private static array  $logArr       = [];
    private static array  $logRootPath  = [];//协程日志根目录
    private static array  $logExtPath   = [];//协程日志根目录02
    private static array  $logExtType   = [];//协程日志
    private static string $coroutineKey = 'logs';//协程标签组成

    /**
     * 协程单例模式处理
     * @param string $logExtTypePath
     */
    public static function getInstance(string $logExtTypePath = 'Api/')
    {
        //协程id
        $coroutine_id = Coroutine::id();
        if (!isset(self::$logExtType[$coroutine_id])) {
            self::$logExtType[$coroutine_id] = $logExtTypePath;

            Coroutine::defer(function () use ($coroutine_id) {
                unset(
                    self::$logArr[$coroutine_id],
                    self::$logRootPath[$coroutine_id],
                    self::$logExtPath[$coroutine_id],
                    self::$logExtType[$coroutine_id],
                );
            });
        }
    }

    /**
     * 日志级别：普通
     * @param string $logTitle 日志名称
     * @param string $logName 日志目录
     * @param string $logFile 日志文件名
     * @param mixed $content 日志参数
     */
    public static function info(string $logTitle = '', string $logName = 'Common', string $logFile = 'common', $content = [])
    {
        self::writeLog($content, $logName, $logFile, $logTitle, 'local.INFO');
    }

    /**
     * 日志级别：警告
     * @param string $logTitle 日志名称
     * @param string $logName 日志目录
     * @param string $logFile 日志文件名
     * @param mixed $content 日志参数
     */
    public static function warning(string $logTitle = '', string $logName = 'Common', string $logFile = 'common', $content = [])
    {
        self::writeLog($content, $logName, $logFile, $logTitle, 'local.WARNING');
    }

    /**
     * 日志级别：错误
     * @param string $logTitle 日志名称
     * @param string $logName 日志目录
     * @param string $logFile 日志文件名
     * @param mixed $content 日志参数
     */
    public static function error(string $logTitle = '', string $logName = 'Common', string $logFile = 'common', $content = [])
    {
        self::writeLog($content, $logName, $logFile, $logTitle, 'local.ERROR');
    }

    /**
     * 日志内容保存
     * @param mixed $content
     * @param string $logName 日志模块名称
     * @param string $logFile 日志内容
     * @param string $logTitle 日志内容头部
     * @param string $status
     *
     */
    private static function writeLog($content, string $logName = 'Common', string $logFile = 'common', string $logTitle = '', string $status = 'INFO')
    {
        $coroutine_id = Coroutine::id();
        $logPath      = $logName . '_' . $logFile;
        $logWhite     = self::whiteLog();
        if (in_array($logPath, $logWhite)) return;//验证日志黑名单
        //if(is_array($content) && isset($content['route']) && $content['route'] == 'Param_uploadFileImg') return;//黑名单
        //获取日志文件目录
        if (empty(self::$logRootPath[$coroutine_id])) {
            self::$logRootPath[$coroutine_id] = realpath('') . '/runtime/logs/Log/' . self::$logExtType[$coroutine_id];
        }

        if (empty(self::$logExtPath[$coroutine_id])) {
            self::$logExtPath[$coroutine_id] = realpath('') . '/runtime/logs/Log02/' . self::$logExtType[$coroutine_id];
        }

        if (empty(self::$logArr[$coroutine_id][$logName][$logFile])) {
            self::$logArr[$coroutine_id][$logName][$logFile] = '================ Start ================' . PHP_EOL;
        }
        $content = is_array($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content;
        if (!empty($logTitle)) $logTitle .= '：';
        self::$logArr[$coroutine_id][$logName][$logFile] .= '[' . (date('Y-m-d H:i:s') . "] {$status}: {$logTitle}{$content}" . PHP_EOL . PHP_EOL);
    }

    public static function close()
    {
        $coroutine_id = Coroutine::id();
        try {
            if (empty(self::$logArr[$coroutine_id])) return;
            //没有Log目录 创建Log02目录
            if (!file_exists(self::$logRootPath[$coroutine_id])) {
                $tem_path = self::$logExtPath[$coroutine_id];
                if (!file_exists($tem_path)) {
                    mkdir($tem_path, 0777, true);
                }
                $logPath = $tem_path . date('Y_m_d');
            } else {
                $logPath = self::$logRootPath[$coroutine_id] . date('Y_m_d');
            }

            //创建日志文件目录
            if (!file_exists($logPath)) mkdir($logPath);

            //逐个日志文件写入
            foreach (self::$logArr[$coroutine_id] as $key => $value) {
                $logDir = $logPath . '/' . $key;
                if (!file_exists($logDir)) mkdir($logDir, 0777, true);
                self::logEnd($value, $logDir);
            }
            self::$logArr[$coroutine_id] = [];
        } catch (\Exception $e) {
            return;
        }
    }

    public static function logEnd($data, $logDir)
    {
        foreach ($data as $k => $v) {
            //获取日志文件名 避免单个日志文件太大
            $count = 1;
            while (true) {
                //生成日志文件名
                $logFile = "{$logDir}" . '/' . "{$k}_{$count}.log";

                //第一次写入日志
                if (!is_file($logFile)) break;

                //日志文件未大于1M
                $file = filesize($logFile) / 1024 / 1024;
                if ($file < self::$size) break;

                $count++;
            }
            $v = rtrim($v);
            $v .= PHP_EOL . '================ End ================' . PHP_EOL . PHP_EOL;

            error_log($v, 3, $logFile);
//            $ch = fopen($logFile, 'ab');
//            fwrite($ch, $v);
//            fclose($ch);
        }
    }

    /**
     * 日志黑名单
     * @return array
     */
    public static function whiteLog()
    {
        return [];
    }
}